package com.h3c.ptability.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.eos.common.constant.Constant;
import com.eos.common.custom.BusinessException;
import com.h3c.ptability.bo.ElectronicSignBO;
import com.h3c.ptability.bo.GetSfsIdsBO;
import com.h3c.ptability.cache.UserCache;
import com.h3c.ptability.config.SpringUtil;
import com.h3c.ptability.config.properties.ElectronicSignProperties;
import com.h3c.ptability.dto.electronicSign.*;
import com.h3c.ptability.dto.electronicSign.callback.CallbackDTO;
import com.h3c.ptability.dto.electronicSign.callback.FinishDocUrlBean;
import com.h3c.ptability.dto.electronicSign.company.ElectronicCompanySignDTO;
import com.h3c.ptability.dto.electronicSign.company.LisSignPos;
import com.h3c.ptability.entity.*;
import com.h3c.ptability.enums.ElectronicSignRecordInfoStatus;
import com.h3c.ptability.enums.ElectronicSignRecordStatus;
import com.h3c.ptability.enums.NeedSignEnum;
import com.h3c.ptability.mapstruct.PtEsignDocFeatureMapstruct;
import com.h3c.ptability.service.*;
import com.h3c.ptability.support.PtEsignDtoPart;
import com.h3c.ptability.support.ReturnCodeEnum;
import com.h3c.ptability.support.event.publisher.FillWaitSignPublisher;
import com.h3c.ptability.utils.AppBusinessException;
import com.h3c.ptability.utils.MqUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import javax.annotation.Resource;
import java.io.File;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author chenyifan
 * @date 2022/5/16 10:01
 */
@Slf4j
@Service
public class ElectronicSignServiceImpl implements IElectronicSignService {

    @Resource(name = "baseElectronic")
    private ElectronicSignProperties electronicSignProperties;

    @Resource
    private IPtEsignDocTemplateService docTemplateService;

    @Resource
    private IPtEsignSceneService sceneService;

    @Resource
    private IPtEsignSceneAttrService sceneAttrService;

    @Resource
    private IPtEsignSceneTemplateService sceneTemplateService;

    @Resource
    private IPtEsignDocFeatureService docFeatureService;

    @Resource
    private IPtEsignRecordService recordService;

    @Resource
    private IPtEsignRecordInfoService recordInfoService;

    @Resource
    private IPtOrgInfoService orgInfoService;

    @Resource
    private IPtEnumService ptEnumService;

    @Resource
    private CommService commService;

    private static final String REQUEST_FLAG = "code";

    private static final String SIGN_FLAG = "status";

    private static final String SIGN_PROCESS_KEY = "signUrls";

    private static final String SIGN_URL_KEY = "signUrl";

    private static final String SIGN_SUCCESS_FLAG = "ok";

    private static final Integer SUCCESS_FLAG = 20220;

    private static final String FAIL_MESSAGE = "msg";

    private static final String FILES_INFO = "fileNameList";

    private static final String SIGN_FLOW_ID = "signFlowId";

    private static final String ORG_FILE_URL = "downloadDocUrl";

    private static final String ONLY_COMPANY_SIGN = "只需公司静默签章";


    @Override
    public String getElectronicToken() {
        HashMap<String, String> param = new HashMap<>(2);
        param.put("account", electronicSignProperties.getAccount());
        param.put("password", electronicSignProperties.getPassword());
        String resultStr = HttpUtil.post(electronicSignProperties.getTokenUrl(), JSONUtil.toJsonStr(param));
        if (StringUtils.hasText(resultStr)) {
            log.info("电子签名获取token返回值:{}", resultStr);
            JSONObject parseObj = JSONUtil.parseObj(resultStr);
            if (parseObj.getInt(REQUEST_FLAG).equals(SUCCESS_FLAG)) {
                return parseObj.getJSONObject("data").getStr("token");
            }
            return "";
        }
        return "";
    }


    /**
     * 最终整合的签章方法
     *
     * @param jsonObject        用户信息
     * @param sceneType         场景类型
     * @param appId             应用来源
     * @param operatorNameAndId 操作人姓名和工号
     * @return 签名的地址
     * @throws Exception 异常
     */
    private String signFile(JSONObject jsonObject, Integer sceneType, String appId, String operatorNameAndId) throws Exception {
        PtEsignScene suitScene = getConfirmSceneId(jsonObject, sceneType, appId);
        ElectronicSignDTO electronicSignDTO = fixupElectronicSignDTO(jsonObject, suitScene);
        if (Objects.isNull(electronicSignDTO)) {
            //将此次记录保存
            String recordId = recordService.saveEsignRecord(true, jsonObject, sceneType, suitScene.getSceneId(), appId, null, null, PtEsignRecord.ALL_ORG_SIGN, null, null, operatorNameAndId);
            //直接从模板获取文件进行公司签章
            List<PtEsignDocTemplate> templates = docTemplateService.getTemplateListBySceneId(suitScene.getSceneId());
            for (PtEsignDocTemplate template : templates) {
                SpringUtil.getBean(IElectronicSignService.class).orgSignAndUpdateRecordInfo(template, recordId);
            }
            return ONLY_COMPANY_SIGN;
        }
        String backResult = baseSignFile(electronicSignDTO, null, false);
        log.info("电子签返回值:{}", backResult);
        //删除暂存的pdf文件
        for (SignDocs signDocs : electronicSignDTO.getSignDocsList()) {
            FileUtil.del(signDocs.getDocFilekey());
        }
        if (!StringUtils.hasText(backResult)) {
            throw new BusinessException(ReturnCodeEnum.RC400.getCode(), "电子签章返回超时或返回值为空");
        }
        JSONObject parseObj = JSONUtil.parseObj(backResult);
        if (Objects.nonNull(parseObj.getStr(SIGN_FLAG)) && parseObj.getStr(SIGN_FLAG).equals(SIGN_SUCCESS_FLAG)) {
            JSONArray jsonArray = parseObj.getJSONArray(FILES_INFO);
            String signFlowId = parseObj.getStr(SIGN_FLOW_ID);
            String signProcessInfo = parseObj.getStr(SIGN_PROCESS_KEY);
            String signUrl = parseObj.getStr(SIGN_URL_KEY);
            String recordId = recordService.saveEsignRecord(true, jsonObject, sceneType, suitScene.getSceneId(), appId, null, jsonArray, signFlowId, signUrl, signProcessInfo, operatorNameAndId);
            if (sceneType.equals(PtEsignRecord.ON_BOARDING_SCENE_TYPE)) {
                MqUtils.pushSignStatusToNem(jsonObject.getStr(ElectronicSignBO.WORKER_ID), MqUtils.NEM_SIGNING_CODE);
            } else if (sceneType.equals(PtEsignRecord.TALENT_SCENE_TYPE)) {
                MqUtils.pushSignStatusToNemTalent(jsonObject.getStr(ElectronicSignBO.WORKER_ID), MqUtils.TALENT_SIGN_ING, "");
            } else if (sceneType.equals(PtEsignRecord.EMPLOYEE_HANDBOOK_TYPE) ||
                    sceneType.equals(PtEsignRecord.RENEW_TYPE) ||
                    sceneType.equals(PtEsignRecord.TRAINING_SERVICE_AGREEMENT_TYPE)) {
                if (sceneType.equals(PtEsignRecord.EMPLOYEE_HANDBOOK_TYPE) ||
                        sceneType.equals(PtEsignRecord.TRAINING_SERVICE_AGREEMENT_TYPE)) {
                    FillWaitSignPublisher.publishFillWaitSignEvent(jsonObject.getStr(ElectronicSignBO.WORKER_ID), sceneType, recordId, null, false);
                }
                MqUtils.pushSignInfoToHrSign(jsonObject.getStr(ElectronicSignBO.WORKER_ID), recordId, sceneType);
            } else if (sceneType.equals(PtEsignRecord.INTERNSHIP_BEFORE_AGREEMENT_TYPE) ||
                    sceneType.equals(PtEsignRecord.INTERNSHIP_AFTER_AGREEMENT_TYPE) ||
                    sceneType.equals(PtEsignRecord.CAMPUS_HEADHUNTING_TYPE)) {
                FillWaitSignPublisher.publishFillWaitSignEvent(null, sceneType, recordId, jsonObject.getStr(ElectronicSignBO.ID_CARD), false);
            }
            return signProcessInfo;
        } else {
            throw new BusinessException(ReturnCodeEnum.RC400.getCode(), "请求电子签章接口失败" + parseObj.getStr(FAIL_MESSAGE));
        }
    }

    /**
     * 解析回调公司静默签章方法
     *
     * @param result 返回字符串结果
     */
    private void parseOrgSignResult(String result, String docFileKey) {
        if (StringUtils.hasText(result)) {
            JSONObject parseObj = JSONUtil.parseObj(result);
            if (Objects.nonNull(parseObj.getStr(SIGN_FLAG)) &&
                    org.apache.commons.lang3.StringUtils.equals(SIGN_SUCCESS_FLAG, parseObj.getStr(SIGN_FLAG))) {
                String downloadDocUrl = parseObj.getStr(ORG_FILE_URL);
                uploadToSfsAndUpdateRecordInfo(downloadDocUrl, docFileKey, false);
            } else {
                recordInfoService.updateRecordInfoFail(docFileKey);
            }
        } else {
            recordInfoService.updateRecordInfoFail(docFileKey);
        }
    }

    /**
     * 解析主动公司静默签章方法
     *
     * @param result 返回字符串结果
     */
    private void parseOrgSignResult(String result, String recordId, String templateId) {
        if (StringUtils.hasText(result)) {
            JSONObject parseObj = JSONUtil.parseObj(result);
            if (Objects.nonNull(parseObj.getStr(SIGN_FLAG)) &&
                    org.apache.commons.lang3.StringUtils.equals(SIGN_SUCCESS_FLAG, parseObj.getStr(SIGN_FLAG))) {
                String downloadDocUrl = parseObj.getStr(ORG_FILE_URL);
                uploadToSfsAndUpdateRecordInfo(downloadDocUrl, recordId, templateId);
            } else {
                recordInfoService.updateRecordInfoFail(recordId, templateId);
            }
        } else {
            recordInfoService.updateRecordInfoFail(recordId, templateId);
        }
    }

    /**
     * 基础电子签章方法
     *
     * @param electronicSignDTO 表单请求参数
     * @return 返回请求字符串结果
     * @throws Exception 报错信息
     */
    private String baseSignFile(ElectronicSignDTO electronicSignDTO,
                                ElectronicCompanySignDTO companySignDTO,
                                boolean isOrg) throws Exception {
        String backResult = "";
        String signUrl;
        if (!isOrg) {
            signUrl = electronicSignProperties.getSignUrl();
            electronicSignDTO.setRedirectUrl(electronicSignProperties.getRedirectUrl());
            electronicSignDTO.setInitiatorAccountId(electronicSignProperties.getInitiatorAccountId());
            electronicSignDTO.setCallbackUrl(electronicSignProperties.getCallbackUrl());
        } else {
            signUrl = electronicSignProperties.getOrgSignUrl();
        }
        HashMap<String, Object> result = new LinkedHashMap<>(20);
        String token = getElectronicToken();
        if (StringUtils.hasText(token)) {
            Map<String, Object> formParam;
            if (!isOrg) {
                formParam = turnObjectToForm(electronicSignDTO, ElectronicSignDTO.class, "", result);
            } else {
                formParam = turnObjectToForm(companySignDTO, ElectronicCompanySignDTO.class, "", result);
            }
            String body = HttpRequest.post(signUrl)
                    .form(formParam)
                    .header(Constant.AUTHORIZATION, "Bearer " + token)
                    .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
                    .execute().body();
            if (StringUtils.hasText(body)) {
                backResult = body;
            }
            return backResult;
        }
        throw new BusinessException(ReturnCodeEnum.RC400.getCode(), "获取电子签名token失败");
    }


    /**
     * 组装公司电子签章数据
     *
     * @param docUrlBean 组装元素对象
     * @return 组装得对象
     */
    private ElectronicCompanySignDTO fixupElectronicCompanySignDTO(FinishDocUrlBean docUrlBean) {
        ElectronicCompanySignDTO companySignDTO = new ElectronicCompanySignDTO();
        //下载返回的签名文件
        File downloadFileFromUrl = HttpUtil.downloadFileFromUrl(docUrlBean.getDownloadDocUrl(), this.getClass().getResource("/").toString() + "/temp/callback/" + IdUtil.randomUUID() + ".pdf");

        PtEsignRecordInfo recordInfo = recordInfoService.getOne(new QueryWrapper<PtEsignRecordInfo>().lambda()
                .eq(PtEsignRecordInfo::getUserDocFileKey, docUrlBean.getDocFileKey())
        );
        List<PtEsignDocFeature> docFeatures = docFeatureService.getOrgTemplateFeatureList(recordInfo.getTemplateId());

        PtOrgInfo orgInfo = orgInfoService.getOne(new QueryWrapper<PtOrgInfo>().lambda()
                .eq(PtOrgInfo::getOrgName, docFeatures.get(0).getOrgInfoName())
        );
        companySignDTO
                .setFile(downloadFileFromUrl)
                .setFileName(recordInfo.getTemplateName().concat(".pdf"))
                .setSubject("公司静默签署回调流程")
                .setAccountId(electronicSignProperties.getAccountId())
                .setAuthorizationOrganizeId(orgInfo.getAuthorizationOrganizeId())
                .setAutoSign("true");

        ArrayList<LisSignPos> lisSignPos = new ArrayList<>();
        for (PtEsignDocFeature docFeature : docFeatures) {
            lisSignPos.add(PtEsignDocFeatureMapstruct.INSTANCE.docFeature2LisSignPos(docFeature).setSealId(orgInfo.getSealId()));
        }
        companySignDTO.setLisSignPos(lisSignPos);
        return companySignDTO;
    }


    /**
     * 组装公司电子签章数据
     *
     * @param template 公司对象组装元素
     * @return 公司对象组装结果
     */
    private ElectronicCompanySignDTO fixupElectronicCompanySignDTO(PtEsignDocTemplate template) {
        ElectronicCompanySignDTO companySignDTO = new ElectronicCompanySignDTO();
        //下载返回的签名文件
        File templateFile = FileUtil.writeBytes(template.getDocFile(), this.getClass().getResource("/").toString() + "/temp/callback/" + IdUtil.randomUUID() + ".pdf");

        List<PtEsignDocFeature> docFeatures = docFeatureService.getOrgTemplateFeatureList(template.getTemplateId());
        PtOrgInfo orgInfo = orgInfoService.getOne(new QueryWrapper<PtOrgInfo>().lambda()
                .eq(PtOrgInfo::getOrgName, docFeatures.get(0).getOrgInfoName())
        );
        companySignDTO
                .setFile(templateFile)
                .setFileName(template.getDocName().concat(".pdf"))
                .setSubject("公司静默签署回调流程")
                .setAccountId(electronicSignProperties.getAccountId())
                .setAuthorizationOrganizeId(orgInfo.getAuthorizationOrganizeId())
                .setAutoSign("true");
        ArrayList<LisSignPos> lisSignPos = new ArrayList<>();
        for (PtEsignDocFeature docFeature : docFeatures) {
            lisSignPos.add(PtEsignDocFeatureMapstruct.INSTANCE.docFeature2LisSignPos(docFeature).setSealId(orgInfo.getSealId()));
        }
        companySignDTO.setLisSignPos(lisSignPos);
        return companySignDTO;
    }


    @Override
    public void callbackFinish(CallbackDTO callbackDTO) {
        //判断流程有无需要公司签章的
        Map<String, List<String>> recordIsOver = recordService.judgeRecordIsOver(callbackDTO.getFlowId() + "");
        log.info("判断是否完结:{}", JSONUtil.toJsonStr(recordIsOver));
        if (recordIsOver.get("UN_OVER").size() == 0) {
            log.info("全部都是个人签署即可");
            //全部都是个人签署即可
            for (FinishDocUrlBean docUrlBean : callbackDTO.getFinishDocUrlBeans()) {
                SpringUtil.getBean(IElectronicSignService.class).uploadToSfsAndUpdateRecordInfo(docUrlBean.getDownloadDocUrl(), docUrlBean.getDocFileKey(), false);
            }
            PtEsignRecord ptEsignRecord = new PtEsignRecord();
            ptEsignRecord.setSignStatus(ElectronicSignRecordStatus.SIGN_SUCCESS)
                    .setSignFinishTime(new Date());
            recordService.updateRecordByFlowId(callbackDTO.getFlowId() + "", ptEsignRecord);
            String workerId = recordService.getWorkerIdByFlowId(callbackDTO.getFlowId() + "");
            MqUtils.pushSignStatusToNem(workerId, MqUtils.NEM_SIGN_SUCCESS_CODE);
            return;
        }
        recordService.saveCallBackParams(callbackDTO.getFlowId() + "", callbackDTO.getFinishDocUrlBeans(), false);
        //将只需个人签章的变更状态为完成
        for (FinishDocUrlBean docUrlBean : callbackDTO.getFinishDocUrlBeans()) {
            if (recordIsOver.get("OVER").contains(docUrlBean.getDocFileKey())) {
                log.info("开始更新docFileKey为:{}的文档状态", docUrlBean.getDocFileKey());
                SpringUtil.getBean(IElectronicSignService.class).uploadToSfsAndUpdateRecordInfo(docUrlBean.getDownloadDocUrl(), docUrlBean.getDocFileKey(), false);
            }
        }
        //将还需公司签章的状态设为待公司签章
        for (FinishDocUrlBean docUrlBean : callbackDTO.getFinishDocUrlBeans()) {
            if (recordIsOver.get("UN_OVER").contains(docUrlBean.getDocFileKey())) {
                recordInfoService.update(new PtEsignRecordInfo().setStatus(ElectronicSignRecordInfoStatus.WAIT_ORG_SIGN),
                        new QueryWrapper<PtEsignRecordInfo>().lambda()
                                .eq(PtEsignRecordInfo::getUserDocFileKey, docUrlBean.getDocFileKey()));
            }
        }
    }

    @Override
    public void callbackFinish2(CallbackDTO callbackDTO) {
        Boolean isWaste = false;
        if (callbackDTO.getFlowType().equals(CallbackDTO.CANCEL_FLOW_CALLBACK)) {
            isWaste = true;
        }
        recordService.saveCallBackParams(callbackDTO.getFlowId() + "", callbackDTO.getFinishDocUrlBeans(), isWaste);
        for (FinishDocUrlBean docUrlBean : callbackDTO.getFinishDocUrlBeans()) {
            log.info("开始更新docFileKey为:{}的文档状态", docUrlBean.getDocFileKey());
            SpringUtil.getBean(IElectronicSignService.class).uploadToSfsAndUpdateRecordInfo(docUrlBean.getDownloadDocUrl(), docUrlBean.getDocFileKey(), isWaste);
        }
    }

    @Override
    @Async("taskExecutor")
    public void orgSignAndUpdateRecordInfo(PtEsignDocTemplate template, String recordId) {
        try {
            ElectronicCompanySignDTO companySignDTO = fixupElectronicCompanySignDTO(template);
            String result = baseSignFile(null, companySignDTO, true);
            log.info("公司主动请求静默签署返回:{}", result);
            FileUtil.del(companySignDTO.getFile());
            parseOrgSignResult(result, recordId, template.getTemplateId());
        } catch (Exception e) {
            recordInfoService.updateRecordInfoFail(recordId, template.getTemplateId());
            log.error(template.getTemplateId() + "公司主动静默签章失败:{}", e.getMessage());
        }
    }

    @Override
    @Async("taskExecutor")
    public void callBackOrgSignAndUpdateRecordInfo(FinishDocUrlBean docUrlBean) {
        try {
            ElectronicCompanySignDTO companySignDTO = fixupElectronicCompanySignDTO(docUrlBean);
            String result = baseSignFile(null, companySignDTO, true);
            log.info("公司静默签署返回:{}", result);
            FileUtil.del(companySignDTO.getFile());
            parseOrgSignResult(result, docUrlBean.getDocFileKey());
        } catch (Exception e) {
            recordInfoService.updateRecordInfoFail(docUrlBean.getDocFileKey());
            log.error("回调公司+" + docUrlBean.getDocFileKey() + "静默签章失败:{}", e.getMessage());
        }
    }


    @Override
    @Async("taskExecutor")
    public void uploadToSfsAndUpdateRecordInfo(String downloadUrl, String docFileKey, Boolean isWaste) {
        //下载个人签署文件
        File downloadFileFromUrl = HttpUtil.downloadFileFromUrl(downloadUrl, this.getClass().getResource("/").toString() + "/temp/result/" + IdUtil.randomUUID() + ".pdf");
        //上传到sfs
        PtUploadFile fileObj = commService.uploadFileToSfs("-1", downloadFileFromUrl, "-1", electronicSignProperties.getAppId(), "-1");
        String fileId = fileObj.getFileId();
        recordInfoService.updateRecordInfoFinish(fileId, docFileKey, isWaste);
        FileUtil.del(downloadFileFromUrl);
    }

    /**
     * 上传签章的文件到sfs更新记录(公司主动静默签署用)
     *
     * @param downloadUrl 下载url
     * @param recordId    记录id
     * @param templateId  模板id
     */
    private void uploadToSfsAndUpdateRecordInfo(String downloadUrl, String recordId, String templateId) {
        //下载个人签署文件
        File downloadFileFromUrl = HttpUtil.downloadFileFromUrl(downloadUrl, this.getClass().getResource("/").toString() + "/temp/result/" + IdUtil.randomUUID() + ".pdf");
        //上传到sfs
        PtUploadFile fileObj = commService.uploadFileToSfs("-1", downloadFileFromUrl, "-1", electronicSignProperties.getAppId(), "-1");
        String fileId = fileObj.getFileId();
        recordInfoService.updateRecordInfoFinish(fileId, recordId, templateId);
        FileUtil.del(downloadFileFromUrl);
    }

    /**
     * 根据用户信息匹配场景
     *
     * @param appId     应用id
     * @param userInfo  用户信息
     * @param sceneType 场景类型
     * @return 场景id
     */
    private PtEsignScene getConfirmSceneId(JSONObject userInfo, Integer sceneType, String appId) {
        List<PtEsignScene> esignScenes = sceneService.getSceneByTypeAndAppId(sceneType, appId);
        PtEsignScene suitScene = null;
        for (PtEsignScene esignScene : esignScenes) {
            if (sceneAttrService.judgeSuitScene(userInfo, esignScene)) {
                suitScene = esignScene;
                break;
            }
        }
        if (Objects.isNull(suitScene)) {
            throw new BusinessException(ReturnCodeEnum.RC400.getCode(), "员工" + userInfo.getStr("workerId") + "无匹配场景");
        }
        return suitScene;
    }

    /**
     * 组装个人电子签章数据(不支持一份文件签署多个个人)
     *
     * @param userInfo  签署用户信息
     * @param suitScene 匹配场景
     * @return 组装后得对象
     */
    private ElectronicSignDTO fixupElectronicSignDTO(JSONObject userInfo,
                                                     PtEsignScene suitScene) {
        //提交需要个人签署的文件，需要公司签署的在回调中统一签署
        ElectronicSignDTO electronicSignDTO = new ElectronicSignDTO();
        List<PtEsignDocTemplate> docTemplates = getPtEsignSceneTemplateList(suitScene.getSceneId());
        //需要个人签署的文件
        List<PtEsignDocTemplate> needUserSign = docTemplates.stream().filter(s -> s.getNeedUserSign().equals(NeedSignEnum.NEED_SIGN)).collect(Collectors.toList());
        if (needUserSign.size() == 0) {
            //都是只需要公司签章的直接签章
            return null;
        }
        //获取模板特征的list，顺序与模板保持一致
        Map<String, List<PtEsignDocFeature>> featureList = docFeatureService.getPtEsignDocFeatureList(docTemplates.stream().map(PtEsignDocTemplate::getTemplateId).collect(Collectors.toList()));

        ArrayList<SignDocs> signDocsList = new ArrayList<>();
        ArrayList<Signers> signersList = new ArrayList<>();
        HashMap<String, Signers> tempMap = new HashMap<>(5);
        for (PtEsignDocTemplate docTemplate : docTemplates) {
            PtEsignDtoPart dtoPart = docTemplateService.fixTemplateParam(docTemplate, featureList.get(docTemplate.getTemplateId()), userInfo);
            signDocsList.add(dtoPart.getSignDocs());
            for (Signers signers : dtoPart.getSignersList()) {
                String key = signers.getAccountId();
                if (!StringUtils.hasText(key)) {
                    key = signers.getUniqueId();
                }
                if (tempMap.containsKey(key)) {
                    tempMap.get(key).getSignDocDetailsList().addAll(signers.getSignDocDetailsList());
                } else {
                    tempMap.put(key, signers);
                }
            }
        }
        for (String key : tempMap.keySet()) {
            signersList.add(tempMap.get(key));
        }
        electronicSignDTO
                .setName(userInfo.getStr(ElectronicSignBO.WORKER_NAME))
                .setCardId(userInfo.getStr(ElectronicSignBO.ID_CARD))
                .setMobile(userInfo.getStr(ElectronicSignBO.MOBILE))
                .setAccountType("2")
                .setSignDocsList(signDocsList)
                .setSignersList(signersList);
        PtEnum sceneType = ptEnumService.getOne(new QueryWrapper<PtEnum>().lambda()
                .eq(PtEnum::getEnumCode, "ES_SCENE_TYPE")
                .eq(PtEnum::getValue, suitScene.getSceneType())
                .last("limit 1")
        );
        if (Objects.isNull(sceneType)) {
            electronicSignDTO.setSubject(suitScene.getSceneName());
        } else {
            electronicSignDTO.setSubject(StrUtil.format(sceneType.getEnumRemark(), userInfo.getStr(ElectronicSignBO.WORKER_NAME)));
        }
        if (electronicSignProperties.getIsOpenAiSign()) {
            electronicSignDTO.setHandPaintedType("AI");
        }
        return electronicSignDTO;
    }


    /**
     * 根据场景id获取需要个人签署的场景文件模板
     *
     * @param sceneId 场景id
     * @return 场景文件模板
     */
    private List<PtEsignDocTemplate> getPtEsignSceneTemplateList(String sceneId) {
        List<String> templateIdList = sceneTemplateService.list(new QueryWrapper<PtEsignSceneTemplate>().lambda()
                .eq(PtEsignSceneTemplate::getSceneId, sceneId)
                .orderByAsc(PtEsignSceneTemplate::getSort)
        ).stream().map(PtEsignSceneTemplate::getTemplateId).collect(Collectors.toList());
        return docTemplateService.list(new QueryWrapper<PtEsignDocTemplate>().lambda()
                .in(PtEsignDocTemplate::getTemplateId, templateIdList)
        );
    }


    @Override
    public String finalSignFile(ElectronicSignBO electronicSignBO, Boolean isRetry) {
        List<JSONObject> userInfoList = JSONUtil.toList(electronicSignBO.getUserData().getJSONArray(ElectronicSignBO.USER_INFO_LIST), JSONObject.class);
        if (userInfoList.size() == 0) {
            throw new BusinessException(ReturnCodeEnum.RC400.getCode(), "无用户信息");
        }
        ArrayList<String> workerIdList = new ArrayList<>();
        userInfoList.forEach(s -> {
            if (StringUtils.hasText(s.getStr(ElectronicSignBO.WORKER_ID))) {
                workerIdList.add(s.getStr(ElectronicSignBO.WORKER_ID));
            }
        });
        List<String> failWorkerIdList = new ArrayList<>();
        if (!isRetry && workerIdList.size() > 0) {
            failWorkerIdList = recordService.judgeWorkerIdsNormal(workerIdList, electronicSignBO.getSceneType());
            //过滤该场景有异常记录的员工签章请求
            List<String> finalFailWorkerIdList = failWorkerIdList;
            userInfoList = userInfoList.stream().filter(s -> {
                String workerId = s.getStr(ElectronicSignBO.WORKER_ID);
                return !finalFailWorkerIdList.contains(workerId);
            }).collect(Collectors.toList());
            //如果提交的都是异常的直接返回
            if (userInfoList.size() == 0) {
                StringBuilder builder = new StringBuilder();
                for (String workerId : failWorkerIdList) {
                    builder.append(workerId).append(",");
                }
                builder.deleteCharAt(builder.length() - 1);
                return builder.toString();
            }
        }

        //根据配置开启线程处理数据
        int threadSize = electronicSignProperties.getEveryThreadNumber();
        //总数据条数
        int dataSize = userInfoList.size();
        //线程数
        int threadNum;
        if (dataSize % threadSize == 0) {
            //能整除的话
            threadNum = dataSize / threadSize;
        } else {
            //不能整除的话，多加一条处理多余的数据
            threadNum = dataSize / threadSize + 1;
        }
        List<List<JSONObject>> splitList = ListUtil.splitAvg(userInfoList, threadNum);
        String operatorNameAndId = UserCache.getLoginUserNameAndId();
        //遍历分好的数据，每一份开一个线程处理
        for (List<JSONObject> jsonObjects : splitList) {
            ThreadUtil.execute(() -> {
                for (JSONObject jsonObject : jsonObjects) {
                    log.info("用户正在处理电子签章，用户信息:{}", JSONUtil.toJsonStr(jsonObject));
                    try {
                        String signUrl = signFile(jsonObject, electronicSignBO.getSceneType(), electronicSignBO.getAppId(), operatorNameAndId);
                        log.info("用户处理电子签章完成,返回签章地址:{}", signUrl);
                    } catch (Exception e) {
                        String recordId = recordService.saveEsignRecord(false, jsonObject, electronicSignBO.getSceneType(), null, electronicSignBO.getAppId(), e.getMessage(), null, null, null, null, operatorNameAndId);
                        if (electronicSignBO.getSceneType().equals(PtEsignRecord.EMPLOYEE_HANDBOOK_TYPE) ||
                                electronicSignBO.getSceneType().equals(PtEsignRecord.RENEW_TYPE) ||
                                electronicSignBO.getSceneType().equals(PtEsignRecord.TRAINING_SERVICE_AGREEMENT_TYPE)) {
                            if (electronicSignBO.getSceneType().equals(PtEsignRecord.EMPLOYEE_HANDBOOK_TYPE) ||
                                    electronicSignBO.getSceneType().equals(PtEsignRecord.TRAINING_SERVICE_AGREEMENT_TYPE)) {
                                FillWaitSignPublisher.publishFillWaitSignEvent(jsonObject.getStr(ElectronicSignBO.WORKER_ID), electronicSignBO.getSceneType(), recordId, null, false);
                            }
                            MqUtils.pushSignInfoToHrSign(jsonObject.getStr(ElectronicSignBO.WORKER_ID), recordId, electronicSignBO.getSceneType());
                        } else if (electronicSignBO.getSceneType().equals(PtEsignRecord.INTERNSHIP_BEFORE_AGREEMENT_TYPE) ||
                                electronicSignBO.getSceneType().equals(PtEsignRecord.INTERNSHIP_AFTER_AGREEMENT_TYPE) ||
                                electronicSignBO.getSceneType().equals(PtEsignRecord.CAMPUS_HEADHUNTING_TYPE)) {
                            FillWaitSignPublisher.publishFillWaitSignEvent(null, electronicSignBO.getSceneType(), recordId, jsonObject.getStr(ElectronicSignBO.ID_CARD), false);
                        }
                        log.error("电子签章失败:{}", JSONUtil.toJsonStr(e));
                    }
                }
            });
        }

        if (failWorkerIdList.size() > 0) {
            StringBuilder builder = new StringBuilder();
            for (String workerId : failWorkerIdList) {
                builder.append(workerId).append(",");
            }
            builder.deleteCharAt(builder.length() - 1);
            return builder.toString();
        } else {
            return "";
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void callbackReject(CallbackDTO callbackDTO) {
        PtEsignRecord record = recordService.getRecordByFlowId(callbackDTO.getFlowId() + "");
        List<PtEsignRecordInfo> recordInfoList = recordInfoService.getRecordInfosByRecordId(record.getRecordId());
        recordInfoList.forEach(s -> s.setStatus(ElectronicSignRecordInfoStatus.SIGN_FAIL));
        if (callbackDTO.getAccountInfo().getType().equals(1)) {
            recordService.updateById(record.setSignStatus(ElectronicSignRecordStatus.ORG_REFUSE_SIGN)
                    .setFailReason(callbackDTO.getResultDescription())
            );
        } else {
            recordService.updateById(record.setSignStatus(ElectronicSignRecordStatus.REFUSE_SIGN)
                    .setFailReason(callbackDTO.getResultDescription())
            );
        }
        recordInfoService.updateBatchById(recordInfoList);
        if (record.getSceneType().equals(PtEsignRecord.ON_BOARDING_SCENE_TYPE)) {
            MqUtils.pushSignStatusToNem(record.getWorkerId(), MqUtils.NEM_SIGN_REJECT_CODE);
        } else if (record.getSceneType().equals(PtEsignRecord.TALENT_SCENE_TYPE)) {
            MqUtils.pushSignStatusToNemTalent(record.getWorkerId(), MqUtils.TALENT_REJECT_SIGN, callbackDTO.getResultDescription());
        }
        log.info("拒绝签署回调成功");
    }

    @Override
    public void reSignFile(ElectronicSignBO electronicSignBO) {
        List<JSONObject> userInfoList = JSONUtil.toList(electronicSignBO.getUserData().getJSONArray(ElectronicSignBO.USER_INFO_LIST), JSONObject.class);
        for (JSONObject object : userInfoList) {
            String recordId = object.getStr(ElectronicSignBO.RECORD_ID);
            cancelSignRecord(recordId, null);
        }
        finalSignFile(electronicSignBO, true);
    }

    /**
     * 对e签宝签署流程进行作废
     *
     * @param signFlowId 原签署流程id
     * @param reason     作废原因（长度<=500）
     * @return 请求返回结果
     */
    @Override
    public String cancelESign(Long signFlowId, String reason) {
        HashMap<String, Object> param = new HashMap<>(2);
        param.put("reason", reason);
        param.put("signFlowId", signFlowId);
        String token = getElectronicToken();
        String cancelResult = HttpRequest.post(electronicSignProperties.getCancelUrl())
                .body(JSONUtil.toJsonStr(param))
                .header(Constant.AUTHORIZATION, "Bearer " + token)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)
                .execute().body();
        if (StringUtils.hasText(cancelResult)) {
            return cancelResult;
        }
        throw new AppBusinessException("取消e签宝签署流程失败，请稍后再试");
    }

    @Override
    public void cancelSignRecord(String recordIdList, String reason) {
        for (String recordId : StrUtil.splitTrim(recordIdList, ",")) {
            PtEsignRecord record = recordService.getById(recordId);
            if (record.getSignStatus().equals(ElectronicSignRecordStatus.SIGNING) ||
                    record.getSignStatus().equals(ElectronicSignRecordStatus.SIGN_SUCCESS) ||
                    record.getSignStatus().equals(ElectronicSignRecordStatus.WAIT_ORG_SIGN)) {
                String cancelResult = cancelESign(Long.parseLong(record.getSignFlowId()), reason);
                log.info("取消签署返回值:{}", cancelResult);
                if (!JSONUtil.parseObj(cancelResult).getJSONObject("data").getBool("success")) {
                    throw new AppBusinessException("取消e签宝签署流程失败，请稍后再试");
                }
                if (record.getSignStatus().equals(ElectronicSignRecordStatus.SIGNING) ||
                        record.getSignStatus().equals(ElectronicSignRecordStatus.WAIT_ORG_SIGN)) {
                    recordService.deleteRecordAndInfoById(recordId);
                    FillWaitSignPublisher.publishFillWaitSignEvent(null, null, recordId, null, true);
                } else {
                    record.setCancelFlowId(JSONUtil.parseObj(cancelResult)
                            .getJSONObject("data")
                            .getJSONObject("data")
                            .getJSONObject("cancelSignFlow")
                            .getStr("cancelSignFlowId"));
                    List<PtEsignSignProcessInfo> cancelSignProcessList = JSONUtil.toList(JSONUtil.parseObj(cancelResult)
                            .getJSONObject("data")
                            .getJSONObject("data")
                            .getJSONObject("cancelSignFlow")
                            .getJSONArray("signUrls"), PtEsignSignProcessInfo.class);
                    cancelSignProcessList.forEach(s -> s.setSignStatus(PtEsignSignProcessInfo.NO_SIGN));
                    record.setStartCancelTime(new Date());
                    record.setCancelProcessInfo(cancelSignProcessList);
                    recordService.wasteRecordAndInfoById(record, JSONUtil.toList(JSONUtil.parseObj(cancelResult)
                            .getJSONObject("data")
                            .getJSONObject("data")
                            .getJSONObject("cancelSignFlow")
                            .getJSONArray("signDocs"), CancelFileInfo.class).stream().map(CancelFileInfo::getFileKey).collect(Collectors.toList())
                    );
                }
            } else if (record.getSignStatus().equals(ElectronicSignRecordStatus.SIGN_FAIL)) {
                recordService.deleteRecordAndInfoById(recordId);
                FillWaitSignPublisher.publishFillWaitSignEvent(null, null, recordId, null, true);
            }
            if (record.getSceneType().equals(PtEsignRecord.ON_BOARDING_SCENE_TYPE)) {
                MqUtils.pushSignStatusToNem(record.getWorkerId(), MqUtils.WAIT_SIGN_CODE);
            }
        }
    }

    /**
     * e签宝催办签署
     *
     * @param flowId 原签署流程id
     */
    private Boolean reminderSign(Long flowId) {
        HashMap<String, Long> param = new HashMap<>(1);
        param.put("flowId", flowId);
        String token = getElectronicToken();
        String remindResult = HttpRequest.post(electronicSignProperties.getRemindUrl())
                .body(JSONUtil.toJsonStr(param))
                .header(Constant.AUTHORIZATION, "Bearer " + token)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)
                .execute().body();
        if (StringUtils.hasText(remindResult)) {
            return JSONUtil.parseObj(remindResult).getJSONObject("data").getBool("success");
        }
        throw new AppBusinessException("e签宝催办签署失败，请稍后再试");
    }

    @Override
    public void remindSignRecord(String recordId) {
        Boolean success = reminderSign(Long.parseLong(recordService.getById(recordId).getSignFlowId()));
        if (!success) {
            throw new AppBusinessException("e签宝催办签署失败，请稍后再试");
        }
    }

    @Override
    public List<String> getSignSfsIds(GetSfsIdsBO getSfsIdsBO) {
        PtEsignRecord latestRecord = recordService.getOne(new QueryWrapper<PtEsignRecord>().lambda()
                .eq(PtEsignRecord::getWorkerId, getSfsIdsBO.getWorkerId())
                .eq(PtEsignRecord::getSceneType, getSfsIdsBO.getSceneType())
                .eq(PtEsignRecord::getSignStatus, ElectronicSignRecordStatus.SIGN_SUCCESS)
                .orderByDesc(PtEsignRecord::getSignFinishTime)
                .last("limit 1")
        );
        if (Objects.isNull(latestRecord)) {
            return new ArrayList<>();
        }
        return recordInfoService.list(new QueryWrapper<PtEsignRecordInfo>().lambda()
                .eq(PtEsignRecordInfo::getRecordId, latestRecord.getRecordId())
        ).stream().map(PtEsignRecordInfo::getSfsId).collect(Collectors.toList());
    }

    @Override
    public void checkAndSignOrg(List<String> recordIds) {
        for (String recordId : recordIds) {
            PtEsignRecord record = recordService.getById(recordId);
            if (Objects.isNull(record) || record.getSignStatus().equals(ElectronicSignRecordStatus.SIGN_SUCCESS)) {
                continue;
            }
            //获取到还需要公司签章的
            List<PtEsignRecordInfo> recordInfoList = recordInfoService.list(new QueryWrapper<PtEsignRecordInfo>().lambda()
                    .eq(PtEsignRecordInfo::getRecordId, recordId)
                    .isNull(PtEsignRecordInfo::getSfsId));
            for (PtEsignRecordInfo ptEsignRecordInfo : recordInfoList) {
                //组装需要公司签章个人回调的请求参数
                SpringUtil.getBean(IElectronicSignService.class).callBackOrgSignAndUpdateRecordInfo(ptEsignRecordInfo.getDocUrlBean());
            }
            //将只需要公司静默签章的进行操作
            List<PtEsignRecordInfo> recordInfos = recordInfoService.getRecordInfosByRecordId(recordId);
            List<PtEsignRecordInfo> onlyOrgSignTemplateList = recordInfos.stream().filter(s -> Objects.isNull(s.getUserDocFileKey())).collect(Collectors.toList());
            for (PtEsignRecordInfo recordInfo : onlyOrgSignTemplateList) {
                PtEsignDocTemplate template = docTemplateService.getById(recordInfo.getTemplateId());
                SpringUtil.getBean(IElectronicSignService.class).orgSignAndUpdateRecordInfo(template, recordInfo.getRecordId());
            }
        }
    }

    @Override
    public void updateProcess(CallbackDTO callbackDTO) {
        PtEsignRecord record;
        if (callbackDTO.getFlowType().equals(CallbackDTO.CANCEL_FLOW_CALLBACK)) {
            record = recordService.getRecordByCancelFlowId(callbackDTO.getFlowId() + "");
        } else {
            record = recordService.getRecordByFlowId(callbackDTO.getFlowId() + "");
        }
        if (callbackDTO.getFlowType().equals(CallbackDTO.CANCEL_FLOW_CALLBACK)) {
            if (Objects.nonNull(record.getCancelProcessInfo())) {
                record.setCancelProcessInfo(JSONUtil.toList(JSONUtil.toJsonStr(record.getCancelProcessInfo()), PtEsignSignProcessInfo.class));
                for (PtEsignSignProcessInfo signProcessInfo : record.getCancelProcessInfo()) {
                    if (signProcessInfo.getAccountId().equals(callbackDTO.getAccountInfo().getAccountId())) {
                        signProcessInfo.setSignStatus(PtEsignSignProcessInfo.ALREADY_SIGN);
                    }
                }
                List<PtEsignSignProcessInfo> userNoSign = record.getCancelProcessInfo().stream()
                        .filter(s -> "2".equals(s.getAccountType()) && s.getSignStatus().equals(PtEsignSignProcessInfo.NO_SIGN))
                        .collect(Collectors.toList());
                if (!record.getSignStatus().equals(ElectronicSignRecordStatus.WAIT_ORG_SIGN_WASTE)) {
                    if (userNoSign.size() == 0) {
                        List<PtEsignSignProcessInfo> orgNoSign = record.getCancelProcessInfo().stream()
                                .filter(s -> "1".equals(s.getAccountType()) && s.getSignStatus().equals(PtEsignSignProcessInfo.NO_SIGN))
                                .collect(Collectors.toList());
                        if (orgNoSign.size() > 0) {
                            record.setSignStatus(ElectronicSignRecordStatus.WAIT_ORG_SIGN_WASTE);
                            for (PtEsignSignProcessInfo signProcessInfo : orgNoSign) {
                                MqUtils.pushEmailJobToNem(record.getWorkerId(), signProcessInfo.getSignUrl(),
                                        electronicSignProperties.getNoticeNemOrgSignWasteEmailTemplateCode());
                            }
                        } else {
                            record.setSignStatus(ElectronicSignRecordStatus.WASTE_SIGN_FINISH);
                        }
                    }
                }
                if (record.getSignStatus().equals(ElectronicSignRecordStatus.WAIT_ORG_SIGN_WASTE) &&
                        userNoSign.size() > 0) {
                    record.setSignStatus(ElectronicSignRecordStatus.WAIT_USER_SIGN_WASTE);
                }
                recordService.updateById(record);
                log.info("更新作废签署流程回调成功");
            }
        } else {
            if (Objects.nonNull(record.getSignProcessInfo())) {
                record.setSignProcessInfo(JSONUtil.toList(JSONUtil.toJsonStr(record.getSignProcessInfo()), PtEsignSignProcessInfo.class));
                for (PtEsignSignProcessInfo signProcessInfo : record.getSignProcessInfo()) {
                    if (signProcessInfo.getAccountId().equals(callbackDTO.getAccountInfo().getAccountId())) {
                        signProcessInfo.setSignStatus(PtEsignSignProcessInfo.ALREADY_SIGN);
                    }
                }
                if (!record.getSignStatus().equals(ElectronicSignRecordStatus.WAIT_ORG_SIGN)) {
                    List<PtEsignSignProcessInfo> userNoSign = record.getSignProcessInfo().stream()
                            .filter(s -> "2".equals(s.getAccountType()) && s.getSignStatus().equals(PtEsignSignProcessInfo.NO_SIGN))
                            .collect(Collectors.toList());
                    if (userNoSign.size() == 0) {
                        List<PtEsignSignProcessInfo> orgNoSign = record.getSignProcessInfo().stream()
                                .filter(s -> "1".equals(s.getAccountType()) && s.getSignStatus().equals(PtEsignSignProcessInfo.NO_SIGN))
                                .collect(Collectors.toList());
                        if (orgNoSign.size() > 0) {
                            record.setSignStatus(ElectronicSignRecordStatus.WAIT_ORG_SIGN);
                            for (PtEsignSignProcessInfo signProcessInfo : orgNoSign) {
                                MqUtils.pushEmailJobToNem(record.getWorkerId(), signProcessInfo.getSignUrl(),
                                        electronicSignProperties.getNoticeNemOrgSignEmailTemplateCode());
                            }
                        } else {
                            record.setSignStatus(ElectronicSignRecordStatus.SIGN_SUCCESS);
                        }
                    }
                }
                recordService.updateById(record);
                log.info("更新签署流程回调成功");
            }
        }
    }

    /**
     * 递归将对象转成表单形式的map
     *
     * @param object      对象
     * @param objectClass 对象类型
     * @param prefix      字符串前缀
     * @param result      结果
     * @return 表单提交格式参数
     * @throws Exception 反射失败
     */
    private Map<String, Object> turnObjectToForm(Object object, Class objectClass, String prefix, Map<String, Object> result) throws Exception {
        for (Field field : ReflectUtil.getFields(objectClass)) {
            if (Objects.isNull(ReflectUtil.getFieldValue(object, field))) {
                continue;
            }
            if (field.getType().equals(List.class)) {
                int i = 0;
                for (Object temp : (List) ReflectUtil.getFieldValue(object, field)) {
                    turnObjectToForm(temp,
                            (Class) ((ParameterizedTypeImpl) field.getGenericType()).getActualTypeArguments()[0],
                            prefix + field.getName() + "[" + i + "].",
                            result);
                    i++;
                }
            } else {
                result.put(prefix + field.getName(), field.get(object));
            }
        }
        return result;
    }
}
